/**
 * Vector.java
 * 
 * @date: 2012-02-28
 * @author: Xiaoyu Guo (MUN# 200982155)
 * This file is part of the course project for COMP 6752, 
 * Application of Computer Graphics. All the file is written
 * by myself.
 */
package mun.cs6752.project.model;

/** Defines a fix-sized vector.
 * A vector holds several double values.
 * @author Xiaoyu Guo
 */
public class Vector {
    protected double[] data;
    protected int size;
    
    /** Construct a Vector from given values
     * @param source
     */
    public Vector(double[] source){
        if(source == null || source.length < 1){
            throw new IllegalArgumentException("source can not be empty");
        }
        
        this.size = source.length;
        this.data = new double[this.size];
        for(int i = 0; i < source.length; i++){
            this.data[i] = source[i];
        }
    }
    
    /** Construct a vector with specified size, filled with 0
     * @param size
     */
    public Vector(int size){
        if(size < 1){
            throw new IllegalArgumentException("size must be >= 1");
        }
        this.data = new double[size];
        this.size = size;
    }
    
    /** Get the specified element
     * @param index
     * @return
     */
    public double get(int index){
        if(index < 0 || index >= this.size){
            throw new IllegalArgumentException("index must be in range");
        }
        return this.data[index];
    }
    
    /** Set the specified element
     * @param index
     * @param value
     */
    public void set(int index, double value){
        if(index < 0 || index >= this.size){
            throw new IllegalArgumentException("index must be in range");
        }
        this.data[index] = value;
    }
    
    /** Get the size of the vector
     * @return how many elements in vector, including 0s
     */
    public int size(){
        return this.size;
    }
    
    /** Check whether the specified value is null
     * @param value
     */
    protected void checkNull(Vector value){
        if(value == null){
            throw new IllegalArgumentException("can not perform calculation with null");
        }
    }
    
    /** Check whether the specified vector has the same size with this
     * @param value
     */
    protected void checkSameSize(Vector value){
        checkNull(value);
        if(value.size != this.size){
            throw new IllegalArgumentException("vector must be the same length");
        }
    }
    
    /** Get the length of the vector
     * @return the length
     */
    public double getLength(){
        double result = 0;
        for(int i = 0; i < this.size; i++){
            result += get(i) * get(i);
        }
        result = Math.sqrt(result);
        return result;
    }
    
    /** Gets the unit vector
     * @return
     */
    public Vector getUnitVector(){
        double length = getLength();
        if(length == 0){
            return this;
        }
        Vector result = new Vector(this.size);
        for(int i = 0; i < this.size; i++){
            result.set(i, this.get(i)/length);
        }
        return result;
    }
    
    /** Perform Vector addition
     * @param value
     * @return
     */
    public Vector add(Vector value){
        checkSameSize(value);
        double[] newData = new double[this.size];
        for(int i = 0; i < size; i++){
            newData[i] = get(i) + value.get(i);
        }
        return new Vector(newData);
    }
    
    /** Gets the negative vector
     * @return
     */
    public Vector negate(){
        double[] newData = new double[this.size];
        for(int i = 0; i < size; i++){
            newData[i] = -get(i);
        }
        return new Vector(newData);
    }
    
    public Vector minus(Vector value){
        checkSameSize(value);
        double[] newData = new double[this.size];
        for(int i = 0; i < size; i++){
            newData[i] = get(i) - value.get(i);
        }
        return new Vector(newData);
    }
    
    /** Scalar multiplication
     * @param value
     * @return
     */
    public Vector multiply(double value){
        double[] newData = new double[this.size];
        for(int i = 0; i < size; i++){
            newData[i] = value * get(i);
        }
        return new Vector(newData);
    }
    
    /**
     * Dot production
     * @param value
     * @return
     */
    public double dotProduct(Vector value){
        checkSameSize(value);
        double result = 0.0;
        for(int i = 0; i < size; i++){
            result += get(i) * value.get(i);
        }
        return result;
    }
}